/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
//

const { reject } = require('lodash');
// promise对象的创建
// 1.使用new关键字创建一个Promise对象 ----> promise就是一个类
// 2.Promise对象中需传入回调函数。回调函数就是一个执行器，在每次使用new关键字创建Promise时，promise里执行器会立即执行
// 3.回调函数中需加入2个参数resolve、reject，这2个参数都是函数，调用这个两个函数更改Promise的状态。
/**
 Promise有3种状态：pending待定、fulfilled成功、rejected失败
 promise中的状态只能从pending->fulfilled，pending->rejected
 一旦状态更改后不可在更改
4.resolve将promise状态更改为fulfilled
  reject将promise状态更改为rejected

  在更改状态时需传递对应状态的数据
5.promise.then()方法内部有两个方法，如果promise状态为成功fulfilled则执行第一个方法，如果Promise状态为失败rejected则执行第二个方法。
所以在.then()内部需要去判断promise的对象。
每个promise都可以调用then()方法，所以then()方法存在原型对象上。

6.then()回调函数中需传入参数；成功时需传入成功的结果，失败后需传入失败的原因。
 */

/**
 1.使用class关键字创建promise类
 2.使用constructor接收执行器executor，且执行器需要立即执行
 */

// 8.将状态值定义为常量，定义成常量的好处：1.使用常量代码会有提示；2.频繁使用、复用
const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //成功
const REJECTED = 'rejected'; //失败
class MyPromise {
    // 3.executor此处执行器相当于promise对象中的回调函数
    constructor(executor) {
        // 4.执行器中需传入promise对象2种更改状态的参数。调用的是MyPromise类内部的方法，此处需加this.
        executor(this.resolve, this.reject);
    }
    // 7.resolve和reject函数时用来更改promise状态，因为每个promise内部都有自己独立的状态，所以需要定义一个实例属性来存储promise的状态。status默认值为等待
    status = PENDING;
    value = undefined;
    reason = undefined;
    // 14.添加实例属性保存成功回调和失败回调 successCallback = undefined;failCallback = undefined;
    // 15.将保存成功回调和失败回调的实例属性更改为数组，用于保存多个then方法中的回调
    successCallback = [];
    failCallback = [];
    // 5.添加resolve属性(直接调用，使用箭头函数使后期调用时this指向内部promise对象)
    // 11.在resolve中传递下成功之后的值
    resolve = value => {
        //9.更改status状态。因为promise状态只允许从pending->fulfilled或pengding->rejected
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        // 13.保存成功的值
        this.value = value;
        // 15.判断successCallback是否存在，即在promise中是否存在异步，是否通过promise.then()保存了对应的成功回调，如果存有成功回调则立即执行
        // this.successCallback && this.successCallback(this.value);
        // 16.循环successCallback成功回调函数数组中元素，一次执行successCallback数组中每个函数
        while (this.successCallback.length) this.successCallback.shift()(this.value);
    };
    // 6.添加reject属性
    // 11.在reject中传递下失败之后的值
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        // 保存失败的原因
        this.reason = reason;
        // this.failCallback && this.failCallback(this.value);
        while (this.failCallback.length) this.failCallback.shift()(this.reason);
    };

    // 10.原型上添加then()方法。接收两个回调，分别是promise成功的回调和失败的回调
    then(successCallback, failCallback) {
        //判断状态
        if (this.status === FULFILLED) {
            // 12.接收resolve传递成功的参数
            successCallback(this.value);
        } else if (this.status === REJECTED) {
            failCallback(this.reason);
        }
        // 13.添加promise状态为pending时的回调方法
        else {
            // 14.遇到异步操作时，promise状态为pending
            // 当状态为pending时then()中无法判断调用是调用成功回调，还是失败回调
            // 等异步操作结束后再执行，异步操作结束后如果是成功则调用成功回调，如果是失败则调用失败
            // 将resolve、reject对应的回调方法存起来，this.successCallback = successCallback; this.failCallback = failCallback;
            // 16.将每个then中的回调push到successCallback数组中
            this.successCallback = successCallback;
            this.failCallback = failCallback;
            this.successCallback.push(successCallback);
            this.failCallback.push(failCallback);
        }
    }
}

module.exports = MyPromise;
